#ifndef __SOFTWARESERIAL_H__
#define __SOFTWARESERIAL_H__

#include <Arduino.h>

#define _SS_MAX_RX_BUFF 64 // RX buffer size

class SoftwareSerial : public Stream
{
private:
    uint16_t _receivePin;
    uint16_t _transmitPin;
    GPIO_TypeDef *_receivePinPort;
    uint32_t _receivePinNumber;
    GPIO_TypeDef *_transmitPinPort;
    uint32_t _transmitPinNumber;
    uint32_t _speed;

    uint16_t _buffer_overflow : 1;
    uint16_t _inverse_logic : 1;
    uint16_t _half_duplex : 1;
    uint16_t _output_pending : 1;

    uint8_t _receive_buffer[_SS_MAX_RX_BUFF];
    volatile uint8_t _receive_buffer_tail;
    volatile uint8_t _receive_buffer_head;

    static bool initialised;
    static HardwareTimer timer;
    static SoftwareSerial *active_listener;
    static SoftwareSerial *volatile active_out;
    static SoftwareSerial *volatile active_in;
    static int32_t tx_tick_cnt;
    static volatile int32_t rx_tick_cnt;
    static uint32_t tx_buffer;
    static int32_t tx_bit_cnt;
    static uint32_t rx_buffer;
    static int32_t rx_bit_cnt;
    static uint32_t cur_speed;

public:
    SoftwareSerial(uint16_t receivePin, uint16_t transmitPin, bool inverse_logic = false);
    virtual ~SoftwareSerial();

    void begin(long speed);
    bool listen();
    void end();

    bool isListening()
    {
        return active_listener == this;
    }
    bool stopListening();
    bool overflow()
    {
        bool ret = _buffer_overflow;
        if (ret)
        {
            _buffer_overflow = false;
        }
        return ret;
    }
    int peek();
    virtual size_t write(uint8_t byte);
    virtual int read();
    virtual int available();
    virtual void flush();
    operator bool()
    {
        return true;
    }
    using Print::write;

    static void setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority);
private:
    void send();
    void recv();
    void setTX();
    void setRX();
    void setRXTX(bool input);
    void setSpeed(uint32_t speed);

    static void handleInterrupt();
};

#endif // __SOFTWARESERIAL_H__